﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Linq;
using System.Text;

namespace SinclairCC.AppVSuite.DuplicateRootDetector
{
    public class Program
    {
        private int cursorLeft;
        private int cursorTop;
        private int longestFileNameLength;
        private NameValueCollection arguments;

        public static void Main(string[] args)
        {
            // A unique name for a mutex.
            string mutexName = "{77B267C9-B31B-489C-A82B-AAA6623BB4AB}";

            // Whether the calling thread was granted initial ownership of the mutex.
            bool grantedOwnership = false;

            // Create a new Mutex object, requesting initial ownership.
            System.Threading.Mutex singleInstanceMutex = new System.Threading.Mutex(true, mutexName, out grantedOwnership);

            try
            {
                // If this thread was not granted ownership of the mutex, another instance of the application
                // must already have ownership. In this case, abandon this instance of the application. Otherwise,
                // proceed with starting the application.
                if (!grantedOwnership)
                {
                    Console.WriteLine("Another instance of this application is already running.");
                    return;
                }
                else
                {
                    NameValueCollection argCollection = ProcessArguments(args);
                    Program prog = new Program(argCollection);
                    prog.DoStuff();
                }
            }
            finally
            {
                // Release all resources held by the mutex.
                singleInstanceMutex.Close();
            }

#if DEBUG
            Console.WriteLine();
            Console.WriteLine();
            Console.Write("Press <ENTER> to close this program.");
            Console.ReadLine();
#endif
        }

        public Program(NameValueCollection argCollection)
        {
            this.cursorLeft = Console.CursorLeft;
            this.cursorTop = Console.CursorTop;
            this.longestFileNameLength = -1;
            this.arguments = argCollection;
        }

        private void DoStuff()
        {
            SetupWindow();

            // Remember the original foreground color.
            ConsoleColor foreColor = Console.ForegroundColor;

            if (this.arguments["?"] != null)
            {
                ShowUsage();
                return;
            }

            if (this.arguments["folder"] == null)
            {
                Console.WriteLine("No folder paths provided.");
                return;
            }

#if DEBUG
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Command Line Arguments Passed");
            Console.WriteLine("=============================");
            if (this.arguments.HasKeys())
            {
                string[] keys = arguments.AllKeys;
                foreach (string key in keys)
                {
                    Console.WriteLine("key: \"{0}\"", key);
                    string[] values = arguments.GetValues(key);
                    foreach (string value in values)
                    {
                        Console.WriteLine("\tvalue: \"{0}\"", value);
                    }
                    Console.WriteLine();
                }
            }
            Console.WriteLine();
            Console.WriteLine();
#endif            

            string[] folderPaths = arguments.GetValues("folder");
            this.cursorLeft = Console.CursorLeft;
            this.cursorTop = Console.CursorTop;
            Database db = new Database();
            db.StatusChanged += new Database.StatusChangedEventHandler(DatabaseStatusChanged);
            foreach (string path in folderPaths)
            {
                db.SurveyDirectory(path);
            }

            Console.WriteLine();
            Console.WriteLine();

            int rootFolderCounter = 0;
            foreach (string rootFolderName in db.DuplicatedRootFolderNames)
            {
                rootFolderCounter++;

                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("duplicated root folder: {0}", rootFolderName.ToLower(System.Globalization.CultureInfo.CurrentCulture));
                Console.ForegroundColor = foreColor;

                DataTable fileRows = db.GetSftInfoForRootFolder(rootFolderName);
                int counter = 1;
                foreach (DataRow fileRow in fileRows.Rows)
                {
                    Console.WriteLine();
                    Console.Write("file ");
                    Console.WriteLine("{0:N0}", counter);

                    string tempString = fileRow["SftPath"].ToString();
                    Console.WriteLine("path: {0}", tempString);

                    tempString = FileSize.AutoFormat((long)fileRow["Size"]);
                    Console.WriteLine("size: {0}", tempString);

                    tempString = ((DateTime)fileRow["Created"]).ToString();
                    Console.WriteLine("created: {0}", tempString);

                    tempString = ((DateTime)fileRow["LastModified"]).ToString();
                    Console.WriteLine("last modified: {0}", tempString);

                    counter++;
                }
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
            }

            /*Console.WriteLine(new string('=', Console.WindowWidth));*/

            int packageIdCounter = 0;
            foreach (Guid packageId in db.DuplicatedPackageIds)
            {
                packageIdCounter++;
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("duplicated package ID: {0}", packageId.ToString("D").ToLower(System.Globalization.CultureInfo.CurrentCulture));
                Console.ForegroundColor = foreColor;
                
                DataTable fileRows = db.GetSftInfoForPackageId(packageId);
                int counter = 1;
                foreach (DataRow fileRow in fileRows.Rows)
                {
                    Console.WriteLine();
                    Console.Write("file ");
                    Console.WriteLine("{0:N0}", counter);

                    string tempString = fileRow["SftPath"].ToString();
                    Console.WriteLine("path: {0}", tempString);

                    tempString = FileSize.AutoFormat((long)fileRow["Size"]);
                    Console.WriteLine("size: {0}", tempString);

                    tempString = ((DateTime)fileRow["Created"]).ToString();
                    Console.WriteLine("created: {0}", tempString);

                    tempString = ((DateTime)fileRow["LastModified"]).ToString();
                    Console.WriteLine("last modified: {0}", tempString);

                    counter++;
                }
                
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
            }

            Console.WriteLine(new string('=', Console.WindowWidth));

            System.Text.StringBuilder message = new StringBuilder();
            int fileCount = db.SftFileCount;
            message.Append(string.Format("Processed {0:N0} .SFT file{1} totalling {2}.", fileCount, fileCount == 1 ? string.Empty : "s", FileSize.AutoFormat(db.TotalSftFileSize)));
            message.Append(System.Environment.NewLine);
            if (rootFolderCounter == 0)
            {
                message.Append("No duplicated root folders were found.");
            }
            else
            {
                message.Append(string.Format("{0:N0}", rootFolderCounter));
                message.Append(string.Format(" duplicated root folder{0} {1} found.", rootFolderCounter == 1 ? string.Empty : "s", rootFolderCounter == 1 ? "was" : "were"));
            }

            message.Append(Environment.NewLine);
            if (packageIdCounter == 0)
            {
                message.Append("No duplicated package IDs were found.");
            }
            else
            {
                message.Append(string.Format("{0:N0}", packageIdCounter));
                message.Append(string.Format(" duplicated package ID{0} {1} found.", packageIdCounter == 1 ? string.Empty : "s", packageIdCounter == 1 ? "was" : "were"));
            }
            Console.WriteLine(message);
            Console.WriteLine();


            if (db.HasErrors)
            {
                /*
                DialogResult showErrors = MessageBox.Show("The operation completed with errors. Would you like to see those errors?", "Duplicate Root Detector", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
                if (showErrors == DialogResult.Yes)
                {
                    ErrorDisplayForm edf = new ErrorDisplayForm(this.db.ErrorTable);
                    edf.Show(this);
                }
                */
            }

            this.GenerateHtmlFiles(db);

            db.Dispose();            
        }

        private void GenerateHtmlFiles(Database db)
        {

            if (this.arguments["html"] != null)
            {
                if (this.arguments["xsl"] == null)
                {
                    arguments.Add("xsl", Shared.DefaultStyleSheetPath);
                }

                // Write the database's data to an XML file temporarily.
                string xmlFilePath = System.IO.Path.GetTempFileName();
                db.WriteXmlFile(xmlFilePath);

                Shared.PrepareStylesheetRepository();

                string[] outputPaths = arguments.GetValues("html");
                string[] xslPaths = arguments.GetValues("xsl");
                for (int i = 0; i < outputPaths.Length; i++)
                {
                    string outputFilePath = System.Environment.ExpandEnvironmentVariables(outputPaths[i]);
                    foreach (string xslFilePath in xslPaths)
                    {
                        if (System.IO.File.Exists(xslFilePath))
                        {
                            System.Xml.Xsl.XslCompiledTransform transform = new System.Xml.Xsl.XslCompiledTransform(false);
                            transform.Load(xslFilePath);

                            System.Xml.XmlTextWriter results = new System.Xml.XmlTextWriter(outputFilePath, System.Text.Encoding.Unicode);
                            System.Xml.XmlTextReader input = new System.Xml.XmlTextReader(xmlFilePath);
                            transform.Transform(input, results);
                            input.Close();
                            results.Close();

                            Console.WriteLine("Generated {0}.", outputFilePath);
                        }
                        else
                        {
                            Console.WriteLine("Stylesheet could not be found.");
                            Console.WriteLine("Path: {0}", xslFilePath);
                        }
                    }
                }

                // Try to delete the temporary XML file. Just eat the error if it fails.
                try
                {
                    System.IO.FileInfo fi = new System.IO.FileInfo(xmlFilePath);
                    fi.Attributes = System.IO.FileAttributes.Normal;
                    fi.Delete();
                }
                catch (Exception)
                {
                }
            }
        }


        private void DatabaseStatusChanged(object sender, SearchStatus newStatus)
        {
            longestFileNameLength = Math.Max(longestFileNameLength, newStatus.StatusMessage.Length /*+6*/);
            Console.SetCursorPosition(cursorLeft, cursorTop);
            for (int x = 0; x < (longestFileNameLength / Console.WindowWidth) + 1; x++)
            {
                Console.Write(new string(' ', Console.WindowWidth));
            }

            Console.SetCursorPosition(cursorLeft, cursorTop);

            ConsoleColor currentColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.Write(newStatus.StatusMessage);
            Console.ForegroundColor = currentColor;
        }

        /// <summary>
        /// Configures the window in which the application is running.
        /// </summary>
        private static void SetupWindow()
        {
            // This is the program information header that will be displayed
            // when the program launches.
            List<string> headerLines = new List<string>();
            headerLines.Add("App-V Duplicate Root Folder Detector");
            headerLines.Add("version 0.2");
            headerLines.Add("Sinclair Community College, Copyright 2010-2011");

            // Display the header.
            Console.ForegroundColor = ConsoleColor.White;
            Console.BackgroundColor = ConsoleColor.Black;
            Console.Clear();
            int lineLen = 0;
            for (int i = 0; i < headerLines.Count; i++)
            {
                Console.WriteLine(headerLines[i]);
                lineLen = Math.Max(lineLen, headerLines[i].Length);
            }

            Console.WriteLine(new string('=', lineLen));
        }

        private static void ShowUsage()
        {
            // TODO: Wrap text based on console window width.
            Console.WriteLine();
            Console.WriteLine("Scans a directory of App-V SFT files, looking for root (asset) folders and");
            Console.WriteLine("package IDs that are used more than once.");
            Console.WriteLine();
            Console.WriteLine("All command line options may be preceded by - or /.");
            Console.WriteLine();
            Console.WriteLine("?\tShow this help text.");
            Console.WriteLine();
            Console.WriteLine("folder\tThe path of the folder to be searched. More than one folder argument\n\tmay be specified.");
            Console.WriteLine();
            Console.WriteLine("html\tThe path of the HTML file to be generated.");
            Console.WriteLine();
            Console.WriteLine("xsl\tThe XSL stylesheet to use to create the HTML document. If this argument\n\tis not specified, the default XSL stylesheet is used.");
        }

        private static NameValueCollection ProcessArguments(string[] args)
        {
            NameValueCollection argColl = new NameValueCollection();

            string pattern = "^[\\s]*[-/]{1}(?<argName>[\\w\\?]+)([:=]{1}(?<argValue>.+))?$";
            System.Text.RegularExpressions.Regex regEx = new System.Text.RegularExpressions.Regex(pattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            foreach (string s in args)
            {
                if (regEx.IsMatch(s))
                {
                    System.Text.RegularExpressions.Match m = regEx.Match(s);
                    argColl.Add(m.Groups["argName"].Value, m.Groups["argValue"].Value);
                }
            }

            return argColl;
        }
    }
}
